#ifndef _RAW_READ_H
#define _RAW_READ_H

#include <stdio.h>
#include <windows.h>
#include "raw_header.h"
#include "raw_block.h"

#define RAW_MAXBLK_NUM	256
#define SFP_MAX_SEEK	0x00FFFFFF

typedef DWORD (WINAPI *Callback)(void);

class cRawRead {
public:
	cRawRead() {hfRaw=0; iCurBlock=-1;}
	~cRawRead() {}
	
	/*Open File*/
	int Open(const char *szNewFile) {
		unsigned long ulPrevSize=0, ulPrevPointer=0;

		strcpy(szFileName,szNewFile);

		Close(); //close file if open
		
		//open file
		hfRaw=OpenFile(szFileName,&ofsFileInfo,OF_READ);
		int iError=GetLastError();
		//if(hfRaw==HFILE_ERROR) return iError;
		
		if(!iError) { 
			//get file size
			ulFileSize=GetFileSize((HANDLE)hfRaw,0);
			if(ulFileSize==INVALID_FILE_SIZE) {
				iError=GetLastError();
				return iError;
			}

			dFileLen=0;
			
			for(iCurBlock=0; !iError; iCurBlock++) {
				//read the block header
				iError=rbBlock.Read(hfRaw,false); 
				
				//bail if error other than EOF
				if(iError && iError!=ERROR_HANDLE_EOF) return iError;

				//add pointer for this block
				if(iCurBlock) ulPrevPointer=ulBlockPointers[iCurBlock-1];
				ulBlockPointers[iCurBlock]=ulPrevPointer+ulPrevSize;
				ulPrevSize=rbBlock.ulTotalSize;

				if(!iError) {
					dBlockLens[iCurBlock]=rbBlock.dBlockLen; //set block duration
					dFileLen+=rbBlock.dBlockLen; //add to file duration
				}
			}	

			if(iError==ERROR_HANDLE_EOF) iError=0; //clear error if just at EOF
			
			//go back to beginning of file
			SetFilePointer((HANDLE)hfRaw,0,0,FILE_BEGIN);
			iError=GetLastError();
			iBlocks=iCurBlock-1;
			iCurBlock=-1;
		}
		
		return BlockFirst();		
	}

	/*Close File*/
	void Close() {if(hfRaw) CloseHandle((HANDLE)hfRaw);} //close the RAW file

	int BlockSet(int iSetBlock) { //set current block
		if(iSetBlock<0) iSetBlock=0;
		if(iSetBlock>=iBlocks) iSetBlock=iBlocks-1;
		
		if(iCurBlock==iSetBlock) return 0;
		
		unsigned long ulSeek; //seek distance
		int iSign; //seek direction

		if(iSetBlock<iCurBlock+1) { //seek backward
			ulSeek=ulBlockPointers[iCurBlock+1]-ulBlockPointers[iSetBlock];
			iSign=-1;
		}

		else { //seek forward
			ulSeek=ulBlockPointers[iSetBlock]-ulBlockPointers[iCurBlock+1];
			iSign=1;
		}

		iCurBlock=iSetBlock;
		
		if(ulSeek) {
			//seek to block
			while(ulSeek>SFP_MAX_SEEK) { //seek multiple times by max distance
				SetFilePointer((HANDLE)hfRaw,iSign*SFP_MAX_SEEK,0,FILE_CURRENT);
				ulSeek-=SFP_MAX_SEEK;
			}
			SetFilePointer((HANDLE)hfRaw,iSign*ulSeek,0,FILE_CURRENT);
		}

		int iError=GetLastError(); //check for seek error
		if(!iError) { //read block
			iError=rbBlock.Read(hfRaw,true);
		}
		return iError;
	}

	int BlockFirst() {return BlockSet(0);} //go to first block in file
	int BlockLast() {return BlockSet(iBlocks-1);} //go to last block in file

	int BlockPrev() { //skip to previous block
		if(iCurBlock<1) return 0;
		return BlockSet(iCurBlock-1);
	}

	int BlockNext() { //skip to next block
		if(iCurBlock==iBlocks) return 0;
		return BlockSet(iCurBlock+1);
	}	

	int ExportText(const char *szTextName, int iChan, int iPol, unsigned long ulTimeStart, unsigned long ulTimeRange, char *szTitle, unsigned long &ulDone, int &iWorking, Callback lppUpdate) {
		cRawBlock rbExport;
		cRawSample rsSample;
		cVector vReal;
		unsigned long ulBytesRead; int iError;
		char szText[1024];//, szComplex[16];
		
		HFILE hfExport;
		DeleteFile(szTextName); //delete file if it exists
		hfExport=OpenFile(szTextName,&ofsFileInfo,OF_CREATE|OF_WRITE|OF_SHARE_DENY_WRITE);
		if(iError=GetLastError()) return iError;
		
		sprintf(szTitle,"B%03i_C%02i_P%i - Converting samples...",iCurBlock+1,iChan+1,iPol+1);
		lppUpdate();
		
		rbBlock.GetReal(iChan,iPol,ulTimeStart,ulTimeRange,vReal);

		szText[0]='\0';
					
		for(unsigned long ulTime=ulTimeStart; ulTime<(ulTimeStart+ulTimeRange) && iWorking; ulTime++) { //for each time
			ulDone=ulTime-ulTimeStart;

			//write samples for this time
			if(rbBlock.GetSample(iChan,ulTime,1,rsSample)) //get a sample
				sprintf(szText,"\t[ GetSample() Error ]");
					
			else {
				//rsSample.cnSamples[iPol].String(szComplex);
				//sprintf(szText,"\t[ %s = %+8.2f,  %+8.2f]",szComplex,cReal.dReal,cReal.dImag);
				cComplex cReal=vReal.lprgcElements[ulDone];				
				sprintf(szText+strlen(szText),"%.2f,%.2f,",cReal.dReal,cReal.dImag);
			}
						
			if(strlen(szText)>1000) { //write when buffer almost full
				WriteFile((HANDLE)hfExport,szText,strlen(szText),&ulBytesRead,0);
				if(iError=GetLastError()) return iError;
				szText[0]='\0';
			}

			//update progress info
			if(!(ulTime%1000)) {
				sprintf(szTitle,"B%03i_C%02i_P%i - Writing samples to file... (%.2f%%)",
						iCurBlock+1,iChan+1,iPol+1,(double(ulDone)/ulTimeRange)*100.0);

				lppUpdate();
			} 
		}
		
		//write buffer at end
		WriteFile((HANDLE)hfExport,szText,strlen(szText),&ulBytesRead,0);
		if(iError=GetLastError()) return iError;
		szText[0]='\0';

		//close block file
		CloseHandle((HANDLE)hfExport);	
		iError=GetLastError();

		//update progress info
		sprintf(szTitle,"B%03i_C%02i_P%i - Done",iCurBlock+1,iChan+1,iPol+1);
		ulDone=ulTimeRange;
		lppUpdate(); 

		return iError;
	}

	OFSTRUCT ofsFileInfo; //info for file
	cRawBlock rbBlock; //RAW Block structure
	int iBlocks, iCurBlock; //# of blocks in file, and current block
	unsigned long ulFileSize; //total size of file
	double dFileLen; //total duration of file in seconds
	
private:	
	HFILE hfRaw; //handle to RAW file
	char szFileName[2048]; //file name of file
	unsigned long ulBlockPointers[RAW_MAXBLK_NUM]; //pointers to start of each Block in file
	double dBlockLens[RAW_MAXBLK_NUM]; //duration of each block in seconds
};

#endif
